package de.recipeminer.crawling.chefkoch;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.recipeminer.environments.ExecutionContextProvider;
import de.recipeminer.models.chefkoch.ChefkochIngredient;
import de.recipeminer.models.chefkoch.ChefkochIngredientLineItem;
import de.recipeminer.models.chefkoch.ChefkochRecipe;
import de.recipeminer.models.chefkoch.ChefkochUnit;
import de.recipeminer.persistence.Db4oCompanionFactory;
import de.recipeminer.persistence.Db4oContainer;
import org.apache.log4j.Logger;

import java.util.List;
import java.util.Set;

/**
 * A converter to generate recipe instances of the Chefkoch data model out of crawled JSON serialisation of
 * recipes wrapped in ChefkochJsonRecipe objects.
 * First, the serialisation strings are converted from their source encoding to UTF-8. After that, the JSON data
 * structures are parsed against an expected JSON-schema (expressed in POJO classes) using the Gson library. Finally
 * the data fields of these intermediate classes are used to initialised model classes.
 *
 * @author Markus Ackermann
 */
public class ChefkochJsonConverter {
    /** cache variable to keep an initialised Gson instance for several conversions */
    private static Gson gson;
    private static Logger logger = Logger.getLogger(ChefkochJsonConverter.class);
    
    
    public ChefkochRecipe convert(ChefkochJsonRecipe jsonRecipe) {
        RecipeJsonStructure recipeStruct = parse(jsonRecipe.getJsonData());
        long chefkochId = recipeStruct.rezept_id;
        String title = recipeStruct.rezept_name;
        String instructions = recipeStruct.rezept_zubereitung;
        long prepTime = recipeStruct.rezept_preparation_time;
        String difficulty = recipeStruct.rezept_schwierigkeit;
        long servings = recipeStruct.rezept_portionen;

        ChefkochRecipe recipe = new ChefkochRecipe(jsonRecipe.getUUID(), chefkochId, title, instructions, prepTime,
                difficulty, servings);
        List<ChefkochIngredientLineItem> ingLineList = Lists.newLinkedList();
        for(IngredientLineJsonStructure ingStruct : recipeStruct.rezept_zutaten) {
            ChefkochIngredient ing = new ChefkochIngredient(ingStruct.id, ingStruct.name);
            if(ingStruct.eigenschaft != null && ingStruct.eigenschaft.length() > 0) {
                ing.setAnnotation(ChefkochIngredient.Keys.ADDITIONAL_DESCRIPTION, ingStruct.eigenschaft);
            }
            ing.setAnnotation(ChefkochIngredient.Keys.BASIC, ingStruct.ist_basic > 0);
            ing.setAnnotation(ChefkochIngredient.Keys.ADDITIONAL_INFO, ingStruct.has_additional_info);
            
            ChefkochIngredientLineItem ingLine;
            ingLine = recipe.addIngredientLineItem(ing, ingStruct.menge, ChefkochUnit.UNSPECIFIED);
            ingLine.setAnnotation(ChefkochIngredientLineItem.Keys.UNIT_STRING, ingStruct.einheit);
            ingLine.setAnnotation(ChefkochIngredientLineItem.Keys.ADDITIONAL_DESCRIPTION, ingStruct.eigenschaft);
            ingLine.setAnnotation(ChefkochIngredientLineItem.Keys.ADDITIONAL_INFO, ingStruct.has_additional_info);

        }
        recipe.setAnnotation(ChefkochRecipe.Keys.SUBTITLE, recipeStruct.rezept_name2);
        recipe.setAnnotation(ChefkochRecipe.Keys.SHOW_ID, recipeStruct.rezept_show_id);
        recipe.setAnnotation(ChefkochRecipe.Keys.COOKING_TIME, recipeStruct.rezept_cooking_time);
        recipe.setAnnotation(ChefkochRecipe.Keys.RESTING_TIME, recipeStruct.rezept_resting_time);
        return recipe;
    }

    private RecipeJsonStructure parse(String jsonData) {
        RecipeJsonStructure newRecipe = getGson().fromJson(jsonData, ChefkochJsonConverter.RecipeJsonStructure.class);
        return newRecipe;
    }

    private Gson getGson() {
        if(gson == null)
            gson = new GsonBuilder().create();
        return gson;
    }

    public static String extractAndFilterJsonRecipe(String jsonResponse, String... propertiesToDelete) {
        JsonObject root = new JsonParser().parse(jsonResponse).getAsJsonObject();
        JsonObject recipeRoot = root.get("result").getAsJsonArray().get(0).getAsJsonObject();
        for(String propName : propertiesToDelete) {
            recipeRoot.remove(propName);
        }
        return  recipeRoot.toString();
    }   

    @Deprecated
    private ChefkochIngredient extractIngredient(IngredientLineJsonStructure lineStruct) {
        return new ChefkochIngredient(lineStruct.id, lineStruct.name);
    }

    private static class RecipeJsonStructure {
        String rezept_name, rezept_name2, rezept_show_id;
        String rezept_zubereitung, rezept_schwierigkeit;
        long rezept_id, rezept_portionen;
        long rezept_preparation_time, rezept_cooking_time, rezept_resting_time;
        List<Long> rezept_zutaten_is_basic;
        List<IngredientLineJsonStructure> rezept_zutaten;
    }

    private static class IngredientLineJsonStructure {
        long id;
        float menge;
        String name, eigenschaft, einheit;
        short ist_basic;
        boolean has_additional_info;
    }

    public static void main(String[] args) {
        Db4oContainer container = ExecutionContextProvider.currentContainer();
        List<ChefkochRecipe> ckRecipes = container.query(ChefkochRecipe.class);
        printStats(container);
        logger.debug(ckRecipes.size() + "ck found");
        Set<String> ckIDs = Sets.newHashSet();
        for(ChefkochRecipe ckr : ckRecipes ) {
            container.delete(ckr);
        }
        container.commit();
        printStats(container);
        container.reopen();
        printStats(container);
        List<ChefkochJsonRecipe> rawRecipes = container.query(ChefkochJsonRecipe.class);
        ChefkochJsonConverter conv = new ChefkochJsonConverter();
        for(ChefkochJsonRecipe raw : rawRecipes) {
            ChefkochRecipe ckr = conv.convert(raw);    
            Db4oCompanionFactory.getInstance().companionFor(ckr).save();
        }
        printStats(container);
        container.reopen();
        printStats(container);
        container.closeAllSessions();
    }

    private static void printStats(Db4oContainer cont) {
        logger.debug("Raw recipes: " + cont.query(ChefkochJsonRecipe.class).size());
        logger.debug("CK recipes: " + cont.query(ChefkochRecipe.class).size());
    }



}
